package com.zoe.onelink.governance.gateway.route.generator;

import com.google.common.base.Joiner;
import com.zoe.onelink.annotation.gateway.GatewayDynamicRoute;
import com.zoe.onelink.governance.gateway.model.NativeGatewayRoute;
import com.zoe.onelink.governance.gateway.route.GatewayDynamicRouteGenerator;
import com.zoe.onelink.util.StrUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2022</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2022-11-01
 */
@RequiredArgsConstructor
public class AnnotationGatewayDynamicRouteGenerator implements GatewayDynamicRouteGenerator {

    private final ApplicationContext applicationContext;

    @Override
    public List<NativeGatewayRoute> generateNativeRoutes(String serviceName) {
        return this.applicationContext.getBean(RequestMappingHandlerMapping.class)
                .getHandlerMethods().entrySet()
                .stream()
                .filter(e -> !e.getKey().getPatternsCondition().getPatterns().isEmpty())
                .filter(e -> e.getValue().hasMethodAnnotation(GatewayDynamicRoute.class))
                .map(e -> {
                    String pathPredicate = e.getKey().getPatternsCondition().getPatterns().stream()
                            .map(path -> path.replaceAll("\\{.*\\}", "*"))
                            .reduce((s1, s2) -> s1 + "," + s2)
                            .orElse(null);
                    HandlerMethod handlerMethod = e.getValue();
                    GatewayDynamicRoute gatewayRoute = handlerMethod.getMethodAnnotation(GatewayDynamicRoute.class);
                    int idSign = (serviceName + handlerMethod).hashCode();
                    String id = StrUtil.format("{}-anno-{}", serviceName, idSign);

                    // 生成过滤器列表
                    // noinspection ConstantConditions
                    List<NativeGatewayRoute.FiltersDTO> filters = Stream.of(gatewayRoute.filters())
                            .map(NativeGatewayRoute.FiltersDTO::new)
                            .collect(Collectors.toList());

                    // 生成断言列表
                    List<NativeGatewayRoute.PredicatesDTO> predicates = Stream.of(gatewayRoute.predicates())
                            .map(NativeGatewayRoute.PredicatesDTO::new)
                            .collect(Collectors.toList());
                    RequestMapping requestMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
                    // noinspection ConstantConditions
                    if (requestMapping.method().length != 0) {
                        String methods = Joiner.on(',').join(requestMapping.method());
                        predicates.add(new NativeGatewayRoute.PredicatesDTO("Method=" + methods));
                    }
                    predicates.add(new NativeGatewayRoute.PredicatesDTO(StrUtil.format("Path={}", pathPredicate)));

                    // 生成元数据
                    Map<String, Object> routeMetadata = new HashMap<>(2);
                    // 超时时间
                    routeMetadata.put("connect-timeout", gatewayRoute.connectTimeoutSeconds() * 1000);
                    routeMetadata.put("response-timeout", gatewayRoute.responseTimeoutSeconds() * 1000);
                    // 如果元数据配置了超时,将覆盖上面注解配置的超时
                    for (String text : gatewayRoute.metadata()) {
                        String[] arr = text.split("=");
                        routeMetadata.put(arr[0], arr[1]);
                    }
                    return new NativeGatewayRoute()
                            .setId(id)
                            .setUri("lb://" + serviceName)
                            .setOrder(gatewayRoute.order())
                            .setFilters(filters)
                            .setPredicates(predicates)
                            .setMetadata(routeMetadata);
                }).collect(Collectors.toList());
    }

}
